#ifndef _GWBASE_BASE_SLICE_H_
#define _GWBASE_BASE_SLICE_H_

#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <string>
#include <functional>

#include "gwbase/base/hash.h"
#include "gwbase/base/coding.h"

namespace dicom {

class Slice {
 public:
  Slice() : data_(""), size_(0) {}

  explicit Slice(const std::string& s) : data_(s.c_str()), size_(s.size()) {}

  explicit Slice(const char* s) : data_(s), size_(strlen(s)) {}

  Slice(const char* d, size_t s) : data_(d), size_(s) {}

  const char* begin() const { return data_; }
  size_t size() const { return size_; }

  bool empty() const { return size_ == 0; }
  void clear() {
    data_ = "";
    size_ = 0;
  }

  void remove_prefix(size_t n) {
    assert(n <= size_);
    data_ += n;
    size_ -= n;
  }

  void remove_suffix(size_t n) {
    assert(n <= size_);
    size_ -= n;
  }

  char operator[](size_t n) {
    assert(n < size_);
    return data_[n];
  }

  std::string ToString() const { return std::string(data_, size_); }

  int compare(const Slice& b) const;

  size_t hash() const { return dicom::hash(begin(), size()); }

  char ReadChar() {
    assert(size() >= sizeof(char));
    char v = GetChar(begin());
    remove_prefix(sizeof(v));
    return v;
  };

  uint32_t ReadUInt32() {
    assert(size() >= sizeof(uint32_t));
    uint32_t v = GetUInt32(begin());
    remove_prefix(sizeof(v));
    return v;
  };

  uint64_t ReadUInt64() {
    assert(size() >= sizeof(uint64_t));
    uint64_t v = GetUInt64(begin());
    remove_prefix(sizeof(v));
    return v;
  };

  uint32_t PeekUInt32(size_t pos = 0) {
    assert(size() >= pos + sizeof(uint32_t));
    uint32_t v = GetUInt32(begin() + pos);
    return v;
  };

  char PeekChar() {
    assert(size() >= sizeof(char));
    char v = GetChar(begin());
    return v;
  };

 private:
  const char* data_;
  size_t size_;
};

inline bool operator==(const Slice& x, const Slice& y) {
  return ((x.size() == y.size()) &&
          (memcmp(x.begin(), y.begin(), x.size()) == 0));
}

inline bool operator!=(const Slice& x, const Slice& y) { return !(x == y); }

inline bool operator<(const Slice& x, const Slice& y) {
  return x.compare(y) < 0;
}

inline int Slice::compare(const Slice& b) const {
  const size_t min_len = (size_ < b.size_) ? size_ : b.size_;
  int r = memcmp(data_, b.data_, min_len);
  if (r == 0) {
    if (size_ < b.size_)
      r = -1;
    else if (size_ > b.size_)
      r = +1;
  }
  return r;
}
}

#endif  // _GWBASE_BASE_SLICE_H_
